/*
 * Created on Jun 29, 2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package org.scohen.juploadr.app;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.scohen.juploadr.Messages;
import org.scohen.juploadr.event.UploadCompleteEvent;
import org.scohen.juploadr.event.UploadEvent;
import org.scohen.juploadr.event.UploadStatusMonitor;
import org.scohen.juploadr.prefs.Configuration;
import org.scohen.juploadr.ui.MessageUtils;
import org.scohen.juploadr.ui.ReusableUIFactory;
import org.scohen.juploadr.ui.TrayPopupHandler;
import org.scohen.juploadr.ui.thumbnail.Thumbnail;
import org.scohen.juploadr.uploadapi.Account;
import org.scohen.juploadr.uploadapi.CommunicationException;
import org.scohen.juploadr.uploadapi.ImageUploadApi;
import org.scohen.juploadr.uploadapi.ProtocolException;
import org.scohen.juploadr.util.StackListDecorator;

/**
 * @author steve
 * 
 * TODO To change the template for this generated type comment go to Window -
 * Preferences - Java - Code Style - Code Templates
 */
public class JUploadrEventDirector implements UploadStatusMonitor {
    private JUploadr juploadr;
    private ReusableUIFactory reusableUIFactory = ReusableUIFactory.getInstance();
    private List<UploadEvent> failedUploads;
    private StackListDecorator<Thumbnail> clickedOrder = new StackListDecorator<Thumbnail>(2);

    public MouseListener mouseListener = new MouseListener();
    public DropTargetListener dropTargetListener = new DropTargetListener();
    public Listener listener = new GenericListener();
    public KeyListener keyListener = new DefaultKeyListener();

    public JUploadrEventDirector(JUploadr uploader) {
        juploadr = uploader;
        failedUploads = new ArrayList<UploadEvent>();
    }

    public void uploadStarted(UploadEvent e) {
        final ProgressBar progress = juploadr.getProgressBar();
        final int max = (int) new File(e.getSource().getImagePath()).length();
        final String key = e.getSource().getKey();
        Runnable r = new Runnable() {
            public void run() {
                progress.setMaximum(max);
                progress.setSelection(0);
                ImageAttributes attrs = (ImageAttributes) juploadr.getImageAttributes().get(key);
                attrs.setState(UploadImage.STATE_UPLOADING);
                attrs.getButton().redraw();
            }
        };
        reusableUIFactory.getDisplay().asyncExec(r);
    }

    public void uploadStatusChanged(UploadEvent e) {
        final int uploaded = (int) e.getBytesWritten();
        final ProgressBar progress = juploadr.getProgressBar();
        Runnable r = new Runnable() {
            public void run() {
                progress.setSelection(uploaded);
            }
        };
        reusableUIFactory.getDisplay().asyncExec(r);
    }

    public void uploadFinished(final UploadCompleteEvent e) {
        final String key = e.getSource().getKey();
        final Map imageAttributes = juploadr.getImageAttributes();
        // if it's been rescaled, delete the file.
        if (e.getSource() != null && e.getSource().isRescaled()) {
            File rescaled = new File(e.getSource().getImagePath());
            rescaled.delete();
        }
        final ProgressBar progress = juploadr.getProgressBar();
        final ImageAttributes atts = (ImageAttributes) imageAttributes.get(key);
        Runnable r = new Runnable() {
            public void run() {
                progress.setSelection(progress.getMaximum());

                atts.setState(UploadImage.STATE_UPLOADED);
                atts.getButton().redraw();
                progress.setSelection(0);
            }
        };
        ImageUploadApi.getInstance().postProcessUpload(e, atts);
        reusableUIFactory.getDisplay().asyncExec(r);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.scohen.juploadr.event.UploadStatusMonitor#allUploadsComplete()
     */
    public void allUploadsComplete() {

        final Map<String, ? extends UploadImage> imageAttributes = juploadr.getImageAttributes();
        if (failedUploads.isEmpty()) {
            Runnable r = new Runnable() {
                public void run() {
                    ImageUploadApi flickrApi = ImageUploadApi.getInstance();
                    flickrApi.postProcessAllUploads(imageAttributes.values());
                    Account userInfo = flickrApi.getCurrentAccount();
                    try {
                        // updates their stats
                        flickrApi.authenticateCurrentAccount();
                        MessageBox success = new MessageBox(JUploadr.window, SWT.ICON_WORKING);
                        success.setMessage(Messages.getString("juploadr.ui.message.success")); //$NON-NLS-1$
                        success.setText(Messages.getString("juploadr.ui.message.upload.complete")); //$NON-NLS-1$
                        success.open();
                    } catch (ProtocolException e) {
                        // Should not occur -> Means user that uploaded the
                        // photos is no longer valid
                        e.printStackTrace();
                        // de-auth the current user then
                        userInfo.setAuthenticated(false);
                        userInfo.save();
                    } catch (CommunicationException e) {
                        // Should not occur -> Means that the Flickr site could
                        // not be contacted
                    }
                }
            };
            reusableUIFactory.getDisplay().syncExec(r);
        } else {
            displayFailedUploads(imageAttributes);
        }
        if (Configuration.getInstance().getAutoRemoveUploadedPhotos()) {
            Runnable r = new Runnable() {
                public void run() {
                    juploadr.removeUploadedPhotos();
                }
            };
            reusableUIFactory.getDisplay().asyncExec(r);
        }
        resetUploadButton();
    }

    /**
     * @param imageAttributes
     */
    private void displayFailedUploads(Map imageAttributes) {
        StringBuffer errorMessage = new StringBuffer();
        errorMessage.append(Messages.getString(
                "juploadr.ui.message.upload.failed", failedUploads.size(), imageAttributes.size())); //$NON-NLS-1$

        if (failedUploads.size() <= 8) {
            for (int i = 0; i < failedUploads.size(); i++) {
                UploadEvent event = (UploadEvent) failedUploads.get(i);
                errorMessage.append(event.getSource().getImagePath());
                errorMessage.append(": \n\t"); //$NON-NLS-1$
                errorMessage.append(event.getErrorMessage());
                errorMessage.append("\n\n"); //$NON-NLS-1$
            }
        } else {
            // most likely there is something seriously wrong.
            UploadEvent event = (UploadEvent) failedUploads.get(0);
            errorMessage.append(event.getErrorMessage());
        }

        failedUploads.clear();
        final String errorText = errorMessage.toString();
        Runnable r = new Runnable() {
            public void run() {
                MessageUtils.showErrorMessage(errorText);
            }
        };
        reusableUIFactory.getDisplay().asyncExec(r);
    }

    /**
     * 
     */
    private void resetUploadButton() {
        Runnable r = new Runnable() {
            public void run() {
                Button button = juploadr.getUploadButton();
                button.setEnabled(true);
                button.setText(Messages.getString("juploadr.ui.button.upload.text.upload"));
                button.setSize(button.computeSize(SWT.DEFAULT, SWT.DEFAULT));
            }
        };
        reusableUIFactory.getDisplay().syncExec(r);
    }

    public void uploadCancelled() {
        resetUploadButton();
    }

    public void uploadFailed(final UploadEvent e) {
        failedUploads.add(e);
        final Map imageAttributes = juploadr.getImageAttributes();
        Runnable r = new Runnable() {
            public void run() {
                ImageAttributes att = (ImageAttributes) imageAttributes.get(e.getSource().getKey());
                att.setState(UploadImage.STATE_NEW);
                att.getButton().redraw();
            }
        };
        reusableUIFactory.getDisplay().syncExec(r);
    }

    private void handleSelectAll() {
        Iterator iter = juploadr.getImageAttributes().values().iterator();
        while (iter.hasNext()) {
            ((ImageAttributes) iter.next()).setSelected(true);
        }
        juploadr.refresh();
    }

    private void handleShiftClick() {
        ImageAttributes clicked = clickedOrder.pop().getImageAttributes();
        if (!clickedOrder.isEmpty()) {
            ImageAttributes startSelection = clickedOrder.pop().getImageAttributes();

            List<ImageAttributes> photos = new ArrayList<ImageAttributes>(juploadr.getImageAttributes().values());
            Collections.sort(photos, new ImageAttributesRankComparator());

            int direction = (clicked.getRank() - startSelection.getRank() > 0) ? 1 : -1;
            if (direction < 0) {
                ImageAttributes tmp = startSelection;
                startSelection = clicked;
                clicked = tmp;
            }

            int myIndex = photos.indexOf(clicked);
            if (myIndex > 0) {
                for (int i = myIndex - 1; i > 0; i--) {
                    ImageAttributes photo = (ImageAttributes) photos.get(i);
                    if (photo.isSelected() == clicked.isSelected()) {
                        break;
                    } else {
                        photo.toggleSelected();
                    }
                }
                juploadr.refresh();
            }
        }
    }

    /**
     * 
     */
    private void deselectAll() {
        Map photos = juploadr.getImageAttributes();
        if (photos != null) {
            Iterator iter = photos.values().iterator();
            while (iter.hasNext()) {
                ImageAttributes photo = (ImageAttributes) iter.next();
                photo.setSelected(false);
            }
            juploadr.refresh();
        }
    }

    /**
     * @param event
     */
    private void handleFileDrop(DropTargetEvent event) {
        String[] files = (String[]) event.data;
        for (int i = 0; i < files.length; i++) {
            File f = new File(files[i]);
            if (f.isFile()) {
                juploadr.addButton(files[i]);
            } else if (f.isDirectory()) {
                File[] dirFiles = f.listFiles();
                for (int j = 0; j < dirFiles.length; j++) {
                    juploadr.addButton(dirFiles[j].getAbsolutePath());
                }
            }
        }
    }

    private void deleteSelected() {
        if (juploadr.getImageAttributes().size() > 0) {
            Iterator iter = juploadr.getImageAttributes().values().iterator();

            while (iter.hasNext()) {
                ImageAttributes value = (ImageAttributes) iter.next();
                if (value.isSelected()) {
                    iter.remove();
                    value.removeFromWindow();
                }
            }
            juploadr.refresh();
        }
    }

    public class MouseListener extends MouseAdapter {

        public void mouseDown(MouseEvent event) {
            if (event.button == 1 && event.stateMask != SWT.CTRL && event.widget instanceof Thumbnail) {
                Thumbnail t = (Thumbnail) event.widget;
                clickedOrder.push(t);
                if (event.stateMask == SWT.SHIFT) {
                    handleShiftClick();
                }
                t.redraw();
            } else if (event.button == 3 || (event.stateMask == SWT.CTRL && event.button == 1)) {
                // handle right click
                if (event.widget instanceof Thumbnail) {
                    // a thumbnail was right-clicked
                    Thumbnail thumb = (Thumbnail) event.widget;
                    ImageUploadApi.getInstance().thumbnailRightClicked(JUploadr.window, juploadr,
                            thumb.getImageAttributes());
                } else if (juploadr.getImageAttributes().size() > 0) {
                    // we have a right click elsewhere on the window
                    ImageUploadApi.getInstance().windowRightClicked(JUploadr.window, juploadr);
                }
            }
        }

        public void mouseUp(MouseEvent event) {
            // de-select all selected photos
            if (!(event.widget instanceof Thumbnail)) {
                deselectAll();
            }
        }
    }

    public class DropTargetListener extends DropTargetAdapter {
        public void drop(DropTargetEvent event) {
            if (juploadr.getFileTransfer().isSupportedType(event.currentDataType)) {
                handleFileDrop(event);
            }
        }
    }

    public class GenericListener implements Listener {

        public void handleEvent(Event event) {
            Shell window = JUploadr.window;
            switch (event.type) {
            case SWT.MenuDetect:
                new TrayPopupHandler(window, juploadr);
                break;
            case SWT.DefaultSelection:
                window.setVisible(!window.getVisible());
                break;
            }
        }

    }

    public class DefaultKeyListener implements KeyListener {
        public void keyPressed(KeyEvent event) {
            if (event.keyCode == 'a' && ((event.stateMask | SWT.CTRL) != 0)) {
                handleSelectAll();
            } else if (event.keyCode == SWT.DEL) {
                deleteSelected();
            }
        }

        public void keyReleased(KeyEvent event) {

        }
    }

}